﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using TD.Interface.Controls;
using TD.Interface.Credits;
using TD.Interface.Credits.Effects;
using TD.Interface.PopMenus;
using TD.TheGame;

namespace TD.Interface.Menus
{
    internal class MainMenu : MenuBase
    {
        private LevelComponent level;

        public bool InMenu { get; set; }
        private Texture2D background;
        private Dictionary<string, MenuBase> menus;

        private TimeSpan elapsedTime;
        private bool fadingOut = false;
        private bool fadingIn = false;

        private CreditsOverlay overlay;
        private ButtonBase buttonPlay;

        public MainMenu(Game1 game) :
            base(game)
        {
            this.Enabled = true;
            this.Visible = true;
            this.InMenu = true;
            this.fadingIn = false;
            this.fadingOut = false;
        }

        public override void Initialize()
        {
            // Ensure no time has elapsed since we only just created it.
            elapsedTime = new TimeSpan(0);

            // Add the other menus to the menu.
            menus = new Dictionary<string, MenuBase>();
            {
                Highscores highscore = new Highscores((Game1)Game);
                highscore.Exit += new OnExit(this.MenuReturn);
                menus.Add("highscores", highscore);

                QuitMenu quit = new QuitMenu((Game1)Game);
                quit.Exit += new OnExit(this.MenuReturn);
                menus.Add("quit", quit);

                ShopMenu shop = new ShopMenu((Game1)Game);
                shop.Exit += new OnExit(this.ContinueGame);
                menus.Add("shop", shop);

                GameOverMenu gameOver = new GameOverMenu((Game1)Game);
                gameOver.Exit += new OnExit(this.GotoMainMenu);
                menus.Add("gameOver", gameOver);
            }

            // Add all the menus in the list of menus to the game.
            foreach (KeyValuePair<string, MenuBase> holder in menus)
            {
                if (holder.Value != null)
                {
                    base.Game.Components.Add(holder.Value);
                }
            }

            overlay = new CreditsOverlay(Game);
            Game.Components.Add(overlay);

            base.Initialize();
        }

        protected override List<DrawableGameComponent> AddItems()
        {
            int offsetX = 20;
            int offsetY = 126; // (720 - 467) == 253 >> 1 == 126.5 == 126

            List<DrawableGameComponent> items = new List<DrawableGameComponent>();
            {
                // Play
                buttonPlay = new ButtonTop((Game1)base.Game, new Point(offsetX, (offsetY + (0 * 160))), "Play", Vector2.Zero, "Start een potje", Vector2.Zero);
                buttonPlay.MouseClick += new ButtonBase.OnClick(this.ButtonPlay_Click);
                items.Add(buttonPlay);

                // Highscores
                ButtonBase button2 = new ButtonMiddle((Game1)base.Game, new Point(offsetX, (offsetY + (1 * 160))), "Highscores", Vector2.Zero, "Bekijk de beste scores", Vector2.Zero);
                button2.MouseClick += new ButtonBase.OnClick(this.ButtonHighscores_Click);
                items.Add(button2);

                // Quit
                ButtonBase button3 = new ButtonBottom((Game1)base.Game, new Point(offsetX, (offsetY + (2 * 160))), "Quit", Vector2.Zero, "Stoppen met spelen", Vector2.Zero);
                button3.MouseClick += new ButtonBase.OnClick(this.ButtonQuit_Click);
                items.Add(button3);
            }
            return items;
        }

        protected override void LoadContent()
        {
            // Get the level from the services.
            level = (LevelComponent)Game.Services.GetService(typeof(LevelComponent));

            // Load the background image.
            ((Game1)Game).TextureManager.LoadAsset("interface\\menu_background");
            background = ((Game1)Game).TextureManager.UseAsset("interface\\menu_background");

            // Load and play the theme music.
            ((Game1)Game).SongManager.LoadAsset("theme");

            // Fire the events to ensure they get played.
            this.HandleEnabledChanged(this, EventArgs.Empty);
            this.HandleVisibleChanged(this, EventArgs.Empty);

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            if (!menus["shop"].Visible)
            {
                // When the game is over (player could no longer protect his castle, we have to switch to the game over menu.)
                MenuBase gameOverMenu = menus["gameOver"];
                if (!InMenu && level.GameOver)
                {
                    gameOverMenu.Enabled = true;
                    gameOverMenu.Visible = true;
                    InMenu = true;
                }

                /**
                 * DEBUG: Allows us to test the menus quicker.
                 */
#if DEBUG
                string debugMenu = string.Empty;
                if (((Game1)Game).InputManager.IsKeyPressed(Keys.F5)) debugMenu = "quit";
                if (((Game1)Game).InputManager.IsKeyPressed(Keys.F6)) debugMenu = "shop";
                if (((Game1)Game).InputManager.IsKeyPressed(Keys.F7)) debugMenu = "gameOver";
                if (debugMenu != string.Empty)
                {
                    menus[debugMenu].Enabled = true;
                    menus[debugMenu].Visible = true;
                }
#endif

                /**
             * MENUS
             */
                if (!gameOverMenu.Visible)
                {
                    // Switch from game to the menu and vice-versa.
                    if (((Game1)Game).InputManager.IsKeyPressed(Keys.Escape))
                    {
                        base.Visible = !base.Visible;
                        this.InMenu = base.Visible;
                        UpdateCredits();

                        if (!this.InMenu || base.Visible)
                        {
                            foreach (KeyValuePair<string, MenuBase> pair in menus)
                            {
                                MenuBase menu = pair.Value as MenuBase;
                                if (menu != null)
                                {
                                    menu.Enabled = false;
                                    menu.Visible = false;
                                }
                            }
                        }

                        if (base.Visible)
                        {
                            fadingIn = true;
                        }
                    }
                }

                /**
                 * CURSOR
                 */
                {
                    if (this.InMenu)
                    {
                        Cursor.Change("interface\\cursor\\cursor_arrow", CursorType.Arrow);
                    }
                    else
                    {
                        // Determine this by the age...
                        if (level.CurrentAge == Age.Future)
                        {
                            Cursor.Change("interface\\cursor\\crosshair_futuristic", CursorType.Crosshair);
                        }
                        else if (level.CurrentAge == Age.Past)
                        {
                            Cursor.Change("interface\\cursor\\crosshair_historical", CursorType.Crosshair);
                        }
                        else //if (level.currAge == Age.Present)
                        {
                            Cursor.Change("interface\\cursor\\crosshair_modern", CursorType.Crosshair);
                        }
                    }
                }

                // Play or stop the music.
                PlayMusic(!InMenu);
            }

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            // Draw the background.
            spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.Opaque);
            spriteBatch.Draw(background, new Rectangle(0, 0, 1280, 720), Color.White);
            spriteBatch.End();

            // Draw the buttons.
            base.Draw(gameTime);
        }

        protected override void HandleEnabledChanged(object sender, EventArgs e)
        {
            // Disable the play button when the game is over.
            if (level.GameOver)
            {
                buttonPlay.Enabled = false;
            }
        }

        protected override void HandleVisibleChanged(object sender, EventArgs e)
        {
            // Hide the play button when the game is over.
            if (level.GameOver)
            {
                buttonPlay.Visible = false;
            }
        }

        private void PlayMusic(bool stop)
        {
            if (!stop)
            {
                // Play the theme when opening the menu.
                ((Game1)Game).SongManager.Loop("theme");
            }
            else //if (stop)
            {
                // Stop the song when exiting the menu.
                ((Game1)Game).SongManager.Stop();
            }
        }

        private void ButtonPlay_Click(object sender)
        {
            //this.Enabled = false;
            this.Visible = false;
            InMenu = false;
            UpdateCredits();
        }

        private void ButtonHighscores_Click(object sender)
        {
            fadingOut = true;
            UpdateCredits();
        }

        private void ContinueGame(object sender)
        {
            // Ensure we are not in any menu when exiting the shop!
            InMenu = false;
            foreach (KeyValuePair<string, MenuBase> pair in menus)
            {
                MenuBase menu = pair.Value as MenuBase;
                if (menu != null)
                {
                    menu.Enabled = false;
                    menu.Visible = false;
                }
            }

            this.Visible = false;
            UpdateCredits();
        }

        private void GotoMainMenu(object sender)
        {
            // Do something... (Go from the Game Over to Main Menu)
            MenuBase menu = menus["gameOver"];
            menu.Enabled = false;
            menu.Visible = false;
            this.Visible = true;
            UpdateCredits();
        }

        private void ButtonQuit_Click(object sender)
        {
            MenuBase quit = menus["quit"];
            quit.Enabled = true;
            quit.Visible = true;
            this.Enabled = false;
            UpdateCredits();
        }

        private void MenuReturn(object sender)
        {
            Enabled = true;
            Visible = true;
            fadingIn = true;
            UpdateCredits();
        }

        protected override void UpdateItems(List<DrawableGameComponent> items, GameTime gameTime)
        {
            #region Fade Out
            if (fadingOut)
            {
                bool adjusted = false;

                if (elapsedTime.TotalMilliseconds > 2)
                {
                    foreach (ButtonMain button in items)
                    {
                        button.Point = new Point(button.Point.X - 8, button.Point.Y);

                        if (button.Point.X < (-300))
                        {
                            //button.Point = button.Restore;
                            button.Point = new Point(-300, button.Point.Y);
                            adjusted = true;
                        }
                    }
                }

                if (adjusted)
                {
                    // Show the highscores menu.
                    MenuBase menu = menus["highscores"];
                    menu.Enabled = true;
                    menu.Visible = true;
                    this.Visible = false;

                    elapsedTime = new TimeSpan(0);
                    fadingOut = false;
                }
                else
                {
                    elapsedTime += gameTime.ElapsedGameTime;
                }
            }
            #endregion

            #region Fade In
            else if (fadingIn)
            {
                bool adjusted = false;

                if (elapsedTime.TotalMilliseconds > 2)
                {
                    foreach (ButtonMain button in items)
                    {
                        button.Point = new Point(button.Point.X + 8, button.Point.Y);

                        if (button.Point.X >= button.Restore.X)
                        {
                            button.Point = button.Restore;
                            adjusted = true;
                        }
                    }
                }

                if (adjusted)
                {
                    elapsedTime = new TimeSpan(0);
                    fadingIn = false;
                }
                else
                {
                    elapsedTime += gameTime.ElapsedGameTime;
                }
            }
            #endregion
        }

        private void UpdateCredits()
        {
            bool visible = (base.Visible && !fadingOut); // Only show when the main menu is visible.

            foreach (KeyValuePair<string, MenuBase> pair in menus)
            {
                // Sorry, there is no quick nice way.
                if (!visible)
                    break;

                MenuBase menu = pair.Value as MenuBase;
                if (menu != null)
                {
                    if (menu.Visible)
                        visible = false;
                }
            }

            overlay.Visible = visible;
        }
    }
}
